// File:    parser.cpp
// Author:  Brian Vanderburg II
// Purpose: Parser object to help parse expression
//------------------------------------------------------------------------------


// Includes
#include <new>
#include <memory>
#include <iomanip>
#include <cmath>

#include "COMMON/expr/parser.h"
#include "COMMON/expr/node.h"
#include "COMMON/expr/error.h"
#include "COMMON/expr/funclist.h"
#include "COMMON/expr/expr.h"

namespace ExprEval
{

// Private functions to solve locale problems
// ------------------------------------------
namespace
{
bool
expreval_isalpha (
    const char c)
{
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}



bool
expreval_isdigit (
    const char c)
{
    return (c >= '0' && c <= '9');
}



bool
expreval_isalnum(
    const char c)
{
    return expreval_isalpha (c) || expreval_isdigit (c);
}



bool
expreval_isspace (
    const char c)
{
    return (c == ' ') || (c == '\t') || (c == '\r') || (c == '\n');
}



double
expreval_atof(
    const char* str)
{
    bool negative = false;
    double value = 0.0;

    // Skip space
    while (expreval_isspace (*str))
        str++;

    // Check for sign
    if(*str == '-')
    {
        negative = true;
        str++;
    }
    else if(*str == '+')
    {
        str++;
    }

    // The part before the decimal
    while (*str >= '0' && *str <= '9')
    {
        value = (value * 10.0) + (double)(*str - '0');
        str++;
    }

    // Decimal, if any
    if(*str == '.')
    {
        double divisor = 1.0;
        str++;

        // Part after the decimal, if any
        while (*str >= '0' && *str <= '9')
        {
            value = (value * 10.0) + (double)(*str - '0');
            divisor *= 10.0;
            str++;
        }

        value /= divisor;
    }

    // The result
    return negative ? -value : value;
}



double
expreval_atof_using_scientific(
    const char* str_value,
    const char* str_index)
{
    double value = expreval_atof (str_value);
    int index = static_cast<int>(expreval_atof (str_index));

    return (value * std::pow (10, index));
}

}



// Token
//------------------------------------------------------------------------------

// Constructor
Token::Token (TokenType type, std::string::size_type start, std::string::size_type end) :
    m_type (type),
    m_start (start),
    m_end (end)
{}

// Construct identifier token
Token::Token (const std::string &ident, std::string::size_type start, std::string::size_type end) :
    m_type (Token::TypeIdentifier),
    m_ident (ident),
    m_start (start),
    m_end (end)
{}

// Construct value token
Token::Token (double value, std::string::size_type start, std::string::size_type end) :
    m_type (Token::TypeValue),
    m_value (value),
    m_start (start),
    m_end (end)
{}

// Get type
Token::TokenType Token::GetType() const
{
    return m_type;
}

// Get identifier
const std::string &Token::GetIdentifier() const
{
    return m_ident;
}

// Get value
double Token::GetValue() const
{
    return m_value;
}

// Get start
std::string::size_type Token::GetStart() const
{
    return m_start;
}

std::string::size_type Token::GetEnd() const
{
    return m_end;
}

std::ostream& operator << (std::ostream& os, const Token& rhs)
{
    switch(rhs.m_type)
    {
    case Token::TypeOpenParenthesis:  // '('
        {
            os << "(";
            break;
        }
    case Token::TypeCloseParenthesis: // ')'
        {
            os << ")";
            break;
        }
    case Token::TypeEqual:            // '='
        {
            os << "=";
            break;
        }
    case Token::TypePlus:             // '+'
        {
            os << "+";
            break;
        }
    case Token::TypeHyphen:           // '-'
        {
            os << "-";
            break;
        }
    case Token::TypeAsterisk:         // '*'
        {
            os << "*";
            break;
        }
    case Token::TypeForwardSlash:     // '/'
        {
            os << "/";
            break;
        }
    case Token::TypeGT:               // '>'
        {
            os << ">";
            break;
        }
    case Token::TypeGE:               // '>='
        {
            os << ">=";
            break;
        }
    case Token::TypeLT:               // '<'
        {
            os << "<";
            break;
        }
    case Token::TypeLE:               // '<='
        {
            os << "<=";
            break;
        }
    case Token::TypeEQ:               // '=='
        {
            os << "==";
            break;
        }
    case Token::TypeNE:               // '!='
        {
            os << "!=";
            break;
        }
    case Token::TypeAnd:              // '&&'
        {
            os << "&&";
            break;
        }
    case Token::TypeOr:               // '||'
        {
            os << "||";
            break;
        }
    case Token::TypeNot:              // '!'
        {
            os << "!";
            break;
        }
    case Token::TypeHat:              // '^'
        {
            os << "^";
            break;
        }
    case Token::TypeAmpersand:        // '&'
        {
            os << "&";
            break;
        }
    case Token::TypeComma:            // ','
        {
            os << ",";
            break;
        }
    case Token::TypeSemicolon:        // ';'
        {
            os << ";";
            break;
        }
    case Token::TypeIdentifier:       // name
        {
            os << rhs.m_ident;
            break;
        }
    case Token::TypeValue:            // 0.2, .6, 2.1
        {
            os << rhs.m_value;
            break;
        }
    default:
        {
            SHOW_FATAL_ERROR("unrecognized Token");
        }
    }

    return os;
}

// Parser
//------------------------------------------------------------------------------

// Constructor
Parser::Parser(Expression *expr) : m_expr(expr)
{
    if(expr == 0)
    {
        SHOW_FATAL_ERROR("Null pointer of Parser::Parser");
    }
}

// Destructor
Parser::~Parser()
{}

// Parse an expression string
Node*
Parser::Parse (
    const std::string& exstr,
    bool& good_parse)
{
    BuildTokens (exstr, good_parse);
#if 0
    for (unsigned int i = 0; i < m_tokens.size(); ++i)
    {
        std::cout<< "No. " << i
                 << ", token type = " << std::setw(2) << m_tokens[i].GetType()
                 << ", token = \"" << m_tokens[i] << "\"" << std::endl;
    }
#endif

    // Make sure it is not still empty
    if(m_tokens.size() == 0)
    {
        good_parse = false;
        return 0;
    }
    else
    {
        // Parse the range
        return ParseRegion (0, m_tokens.size() - 1, good_parse);
    }
}

// Parse a region of tokens
Node*
Parser::ParseRegion (
    Parser::size_type start,
    Parser::size_type end,
    bool &good_parse)
{
    size_type pos;
    size_type fgopen = (size_type)-1;
    size_type fgclose = (size_type)-1;
    size_type assignindex = (size_type)-1;
    size_type addsubindex = (size_type)-1;
    size_type muldivindex = (size_type)-1;
    size_type andorindex  = (size_type)-1;
    size_type notindex    = (size_type)-1;
    size_type relateindex = (size_type)-1;
    size_type posnegindex = (size_type)-1;
    size_type expindex = (size_type)-1;
    bool multiexpr = false;
    int plevel = 0;

    // Check simple syntax
    if(start > end)
    {
        SHOW_NONFATAL_ERROR ("syntax error");
        good_parse =  false;
        return 0;
    }

    // Scan through tokens
    for (pos = start; pos <= end; pos++)
    {
        switch(m_tokens[pos].GetType())
        {
        case Token::TypeOpenParenthesis:
            {
                plevel++;

                // Opening of first group?
                if(plevel == 1 && fgopen == (size_type)-1)
                    fgopen = pos;

                break;
            };

        case Token::TypeCloseParenthesis:
            {
                plevel--;

                // First group closed?
                if(plevel == 0 && fgclose == (size_type)-1)
                    fgclose = pos;

                if(plevel < 0)
                {
                    good_parse=false;
                    SHOW_NONFATAL_ERROR ("unmatched parenthesis");
                    return 0;
                }

                break;
            }

        case Token::TypeEqual:
            {
                if(plevel == 0)
                {
                    if(assignindex == (size_type)-1)
                        assignindex = pos;
                }

                break;
            }

        case Token::TypeAsterisk:
        case Token::TypeForwardSlash:
            {
                if(plevel == 0)
                {
                    muldivindex = pos;
                }

                break;
            }

        case Token::TypeGE:
        case Token::TypeGT:
        case Token::TypeLE:
        case Token::TypeLT:
        case Token::TypeEQ:
        case Token::TypeNE:
            {
                if(plevel == 0)
                {
                    relateindex = pos;
                }

                break;
            }

        case Token::TypeAnd:
        case Token::TypeOr:
            {
                if(plevel == 0)
                {
                    andorindex = pos;
                }

                break;
            }

        case Token::TypeNot:
            {
                if(plevel == 0)
                {
                    notindex = pos;
                }

                break;
            }

        case Token::TypeHat:
            {
                if(plevel == 0)
                {
                    expindex = pos;
                }

                break;
            }

        case Token::TypePlus:
        case Token::TypeHyphen:
            {
                if(plevel == 0)
                {
                    if(pos == start)
                    {
                        // Positive or negative sign
                        if(posnegindex == (size_type)-1)
                            posnegindex = pos;
                    }
                    else
                    {
                        // What is before us
                        switch(m_tokens[pos - 1].GetType())
                        {
                        case Token::TypeEqual:
                        case Token::TypePlus:
                        case Token::TypeHyphen:
                        case Token::TypeAsterisk:
                        case Token::TypeForwardSlash:
                        case Token::TypeHat:
                        case Token::TypeGT:
                        case Token::TypeGE:
                        case Token::TypeLT:
                        case Token::TypeLE:
                        case Token::TypeEQ:
                        case Token::TypeNE:
                        case Token::TypeAnd:
                        case Token::TypeOr:
                        case Token::TypeNot:
                            // After any of these, we are a positive/negative
                            if(posnegindex == (size_type)-1)
                                posnegindex = pos;
                            break;

                        default:
                            // After any other, we are addition/subtration
                            addsubindex = pos;
                            break;
                        }
                    }
                }

                break;
            }

        case Token::TypeSemicolon:
            {
                if(plevel == 0)
                {
                    multiexpr = true;
                }

                break;
            }
        default:
            {
                break;
            }
        }
    }

    // plevel should be 0
    if(plevel != 0)
    {
        SHOW_NONFATAL_ERROR ("unmatched parenthesis");
        good_parse = false;
        return 0;
    }

    // Parse in certain order to maintain order of operators
    //the order in ascend is:
    // =, ||, &&, !=, ==, >=, >, <=, <, -, +, !,()

    // Multi-expression first
    if(multiexpr)
    {
        std::auto_ptr<Node> n (new MultiNode (m_expr));
        n->Parse (*this, start, end, 0, good_parse);
        return n.release();
    }
    else if(assignindex != (size_type)-1)
    {
        // Assignment next
        std::auto_ptr<Node> n (new AssignNode (m_expr));
        n->Parse (*this, start, end, assignindex, good_parse);
        return n.release();
    }
    else if(andorindex != (size_type)-1)
    {
        if(m_tokens[andorindex].GetType() == Token::TypeOr)
        {
            // "||"
            std::auto_ptr<Node> n (new OrNode (m_expr));
            n->Parse (*this, start, end, andorindex, good_parse);
            return n.release();
        }
        else
        {
            // "&&"
            std::auto_ptr<Node> n (new AndNode (m_expr));
            n->Parse (*this, start, end, andorindex, good_parse);
            return n.release();
        }
    }
    else if(relateindex != (size_type)-1)
    {
        // >, >=, <, <=, ==, !=
        if(m_tokens[relateindex].GetType() == Token::TypeNE)
        {
            // "!="
            std::auto_ptr<Node> n (new NENode (m_expr));
            n->Parse (*this, start, end, relateindex, good_parse);
            return n.release();
        }
        else if(m_tokens[relateindex].GetType() == Token::TypeEQ)
        {
            // "=="
            std::auto_ptr<Node> n (new EQNode (m_expr));
            n->Parse (*this, start, end, relateindex, good_parse);
            return n.release();
        }
        else if(m_tokens[relateindex].GetType() == Token::TypeGE)
        {
            // ">="
            std::auto_ptr<Node> n (new GENode (m_expr));
            n->Parse (*this, start, end, relateindex, good_parse);
            return n.release();
        }

        if(m_tokens[relateindex].GetType() == Token::TypeGT)
        {
            // ">"
            std::auto_ptr<Node> n (new GTNode (m_expr));
            n->Parse (*this, start, end, relateindex, good_parse);
            return n.release();
        }
        else if(m_tokens[relateindex].GetType() == Token::TypeLE)
        {
            // "<="
            std::auto_ptr<Node> n (new LENode (m_expr));
            n->Parse (*this, start, end, relateindex, good_parse);
            return n.release();
        }
        else// if(m_tokens[relateindex].GetType() == Token::TypeLT)
        {
            // "<"
            std::auto_ptr<Node> n (new LTNode (m_expr));
            n->Parse (*this, start, end, relateindex, good_parse);
            return n.release();
        }
    }
    else if(addsubindex != (size_type)-1)
    {
        // Addition/subtraction next
        if(m_tokens[addsubindex].GetType() == Token::TypePlus)
        {
            // Addition
            std::auto_ptr<Node> n (new AddNode (m_expr));
            n->Parse (*this, start, end, addsubindex, good_parse);
            return n.release();
        }
        else
        {
            // Subtraction
            std::auto_ptr<Node> n (new SubtractNode (m_expr));
            n->Parse (*this, start, end, addsubindex, good_parse);
            return n.release();
        }
    }
    else if(muldivindex != (size_type)-1)
    {
        // Multiplication/division next

        if(m_tokens[muldivindex].GetType() == Token::TypeAsterisk)
        {
            // Multiplication
            std::auto_ptr<Node> n (new MultiplyNode (m_expr));
            n->Parse (*this, start, end, muldivindex, good_parse);
            return n.release();
        }
        else
        {
            // Division
            std::auto_ptr<Node> n (new DivideNode (m_expr));
            n->Parse (*this, start, end, muldivindex, good_parse);
            return n.release();
        }
    }
    else if(posnegindex == start)
    {
        // Positive/negative next, must be at start and check before exponent
        if(m_tokens[posnegindex].GetType() == Token::TypePlus)
        {
            // Positive
            return ParseRegion (posnegindex + 1, end, good_parse);
        }
        else
        {
            std::auto_ptr<Node> n (new NegateNode (m_expr));
            n->Parse (*this, start, end, posnegindex, good_parse);
            return n.release();
        }
    }
    else if(notindex != (size_type)-1)
    {
        // "!"
        std::auto_ptr<Node> n (new NotNode (m_expr));
        n->Parse (*this, start, end, notindex, good_parse);
        return n.release();
    }
    else if(expindex != (size_type)-1)
    {
        // Exponent
        std::auto_ptr<Node> n (new ExponentNode (m_expr));
        n->Parse (*this, start, end, expindex, good_parse);
        return n.release();
    }
    else if(posnegindex != (size_type)-1)
    {
        // Check pos/neg again.  After testing for exponent, a pos/neg
        // at plevel 0 is syntax error
        SHOW_NONFATAL_ERROR ("syntax error");
        good_parse = false;
        return 0;
    }
    else if(fgopen == start)
    {
        // Group parenthesis, make sure something in between them
        if(fgclose == end && fgclose > fgopen + 1)
        {
            return ParseRegion (fgopen + 1, fgclose - 1, good_parse);
        }
        else
        {
            SHOW_NONFATAL_ERROR ("syntax error");
            good_parse = false;
            return 0;
        }
    }
    else if(fgopen == start + 1)
    {
        // Function
        if(fgclose == end)
        {
            // Find function list
            FunctionList *flist = m_expr->GetFunctionList();

            if(flist == 0)
            {
                SHOW_NONFATAL_ERROR ("not function list");
                good_parse = false;
                return 0;
            }

            // Get name
            std::string ident = m_tokens[start].GetIdentifier();

            // Create function node
            std::auto_ptr<FunctionNode> n (flist->Create (ident, m_expr));

            if(n.get())
            {
                n->Parse (*this, fgopen, fgclose, 0, good_parse);
                if(!good_parse)
                {
                    //SHOW_NONFATAL_ERROR ("unrecognized syntax of \"" <<ident<< "\"");
                    return 0;
                }
            }
            else
            {
                SHOW_NONFATAL_ERROR ("unrecognized token of \"" <<ident<< "\"");
                good_parse=false;
            }

            return n.release();
        }
        else
        {
            SHOW_NONFATAL_ERROR ("syntax error");
            good_parse = false;
            return 0;
        }
    }
    else if(start == end)
    {
        // Value, variable, or constant

        if(m_tokens[start].GetType() == Token::TypeIdentifier)
        {
            // Variable/constant
            std::auto_ptr<Node> n (new VariableNode (m_expr));
            n->Parse (*this, start, end, 0, good_parse);
            return n.release();
        }
        else
        {
            // Value
            std::auto_ptr<Node> n (new ValueNode (m_expr));
            n->Parse (*this, start, end, 0, good_parse);
            return n.release();
        }
    }
    else
    {
        // Unknown, syntax
        good_parse = false;
        std::ostringstream oss;
        for (Parser::size_type i = start; i <= end; ++i)
        {
            oss << m_tokens[i];
        }

        SHOW_NONFATAL_ERROR ("unrecognized syntax \"" << oss.str() << "\"");
        return 0;
    }
}

// Get a token
const Token &Parser::operator[] (Parser::size_type pos) const
{
    return m_tokens[pos];
}

// Build tokens
void
Parser::BuildTokens (
    const std::string& exstr,
    bool& good_parse)
{
    m_tokens.clear();

    // Test zero-length expression
    if(exstr.length() == 0)
    {
        //throw(EmptyExpressionException());
        good_parse = false;
        SHOW_NONFATAL_ERROR ("empty expression are not supported");
    }

    // Search through list
    std::string::size_type pos;
    const std::string::size_type end_pos=exstr.length()-1;
    bool comment = false;

    for (pos = 0; pos < exstr.length(); pos++)
    {
        // Take action based on character
        switch(exstr[pos])
        {
            // Comment
        case '#':
            {
                comment = true;

                break;
            }

            // Newline ends comment
        case '\r':
        case '\n':
            {
                comment = false;

                break;
            }

            // Open parenthesis
        case '(':
            {
                if(comment == false)
                {
                    m_tokens.push_back(Token (Token::TypeOpenParenthesis, pos, pos));
                }
                break;
            }

            // Close parenthesis
        case ')':
            {
                if(comment == false)
                {
                    m_tokens.push_back(Token (Token::TypeCloseParenthesis, pos, pos));
                }
                break;
            }

            // Equal or EQ, depend on next symbol
        case '=':
            {
                if(comment == false)
                {
                    if((pos!=end_pos) && (exstr[pos+1]=='='))
                    {
                        m_tokens.push_back(Token (Token::TypeEQ, pos, pos+1));
                        ++pos;
                    }
                    else
                    {
                        m_tokens.push_back(Token (Token::TypeEqual, pos, pos));
                    }
                }
                break;
            }

            // Plus
        case '+':
            {
                if(comment == false)
                {
                    m_tokens.push_back(Token (Token::TypePlus, pos, pos));
                }
                break;
            }

            // Hyphen
        case '-':
            {
                if(comment == false)
                {
                    m_tokens.push_back(Token (Token::TypeHyphen, pos, pos));
                }
                break;
            }

            // Asterisk
        case '*':
            {
                if(comment == false)
                {
                    m_tokens.push_back(Token (Token::TypeAsterisk, pos, pos));
                }
                break;
            }

            // Forward slash
        case '/':
            {
                if(comment == false)
                {
                    m_tokens.push_back(Token (Token::TypeForwardSlash, pos, pos));
                }
                break;
            }

            // Greater Than
        case '>':
            {
                if((pos!=end_pos) && (exstr[pos+1]=='='))
                {
                    m_tokens.push_back(Token (Token::TypeGE, pos, pos+1));
                    ++pos;
                }
                else
                {
                    m_tokens.push_back(Token (Token::TypeGT, pos, pos));
                }
                break;
            }

            // Less Than
        case '<':
            {
                if((pos!=end_pos) && (exstr[pos+1]=='='))
                {
                    m_tokens.push_back(Token (Token::TypeLE, pos, pos+1));
                    ++pos;
                }
                else
                {
                    m_tokens.push_back(Token (Token::TypeLT, pos, pos));
                }
                break;
            }

            // Not
        case '!':
            {
                if((pos!=end_pos) && (exstr[pos+1]=='='))
                {
                    m_tokens.push_back(Token (Token::TypeNE, pos, pos+1));
                    ++pos;
                }
                else
                {
                    m_tokens.push_back(Token (Token::TypeNot, pos, pos));
                }
                break;
            }

            // Or
        case '|':
            {
                if((pos!=end_pos) && (exstr[pos+1]=='|'))
                {
                    m_tokens.push_back(Token (Token::TypeOr, pos, pos+1));
                    ++pos;
                }
                else
                {
                    m_tokens.push_back(Token (Token::TypeIdentifier, pos, pos));
                }
                break;
            }

            // Hat (exponent)
        case '^':
            {
                if(comment == false)
                {
                    m_tokens.push_back(Token (Token::TypeHat, pos, pos));
                }
                break;
            }

            // Ampersand
        case '&':
            {
                if(comment == false)
                {
                    if((pos!=end_pos) && (exstr[pos+1]=='&'))
                    {
                        m_tokens.push_back(Token (Token::TypeAnd, pos, pos+1));
                        ++pos;
                    }
                    else
                    {
                        m_tokens.push_back(Token (Token::TypeAmpersand, pos, pos));
                    }
                }
                break;
            }

            // Comma
        case ',':
            {
                if(comment == false)
                {
                    m_tokens.push_back(Token (Token::TypeComma, pos, pos));
                }
                break;
            }

            // Semicolon
        case ';':
            {
                if(comment == false)
                {
                    m_tokens.push_back(Token (Token::TypeSemicolon, pos, pos));
                }
                break;
            }

            // None of the above, but it may be an identifier or value
        default:
            {
                if(comment == false)
                {
                    // First, test for value
                    if(exstr[pos] == '.' || expreval_isdigit (exstr[pos]))
                    {
                        // We are a value
                        std::string::size_type start = pos;

                        // Digits before period
                        while (expreval_isdigit (exstr[pos]))
                            pos++;

                        // Period
                        if(exstr[pos] == '.')
                            pos++;

                        // Digits after period
                        while (expreval_isdigit (exstr[pos]))
                            pos++;

                        std::string value_ident = exstr.substr(start, pos - start);
                        std::string index_ident;
                        bool is_sci = false;

                        //check if it is a scientific notation as 1.3e-5 or -2.E+6 or 3e6
                        if ((exstr[pos] == 'e' || exstr[pos] == 'E') &&
                                (exstr[pos+1] == '-' || exstr[pos+1] == '+' || expreval_isdigit (exstr[pos+1])))
                        {
                            pos++; //jump e or E

                            std::string::size_type index_start = pos;

                            pos++; //jumb + or -
                            while (expreval_isdigit (exstr[pos]))
                            {
                                pos++;
                            }

                            index_ident = exstr.substr(index_start, pos - index_start);
                            is_sci = true;
                        }

                        // Create token
                        if (!is_sci)
                        {
                            m_tokens.push_back(Token (expreval_atof (value_ident.c_str()), start, pos - 1));
                        }
                        else
                        {
                            m_tokens.push_back(Token (expreval_atof_using_scientific (value_ident.c_str(), index_ident.c_str()), start, pos - 1));
                        }

                        // Move pos back so pos++ will set it right
                        pos--;
                    }
                    else if(exstr[pos] == '_' || expreval_isalpha (exstr[pos]))
                    {
                        // We are an identifier
                        std::string::size_type start = pos;
                        bool foundname = true; // Found name part

                        // Search for name, then period, etc
                        // An identifier can be multiple parts.  Each part
                        // is formed as an identifier and seperated by a period,
                        // An identifier can not end in a period
                        //
                        // color1.red : 1 identifier token
                        // color1. : color1 is identifier, . begins new token
                        // color1.1red : Not value (part 2 is not right)
                        while (foundname)
                        {
                            // Part before period
                            while (exstr[pos] == '_' || expreval_isalnum(exstr[pos]))
                                pos++;

                            // Is there a period
                            if(exstr[pos] == '.')
                            {
                                pos++;

                                // There is a period, look for the name again
                                if(exstr[pos] == '_' || expreval_isalpha (exstr[pos]))
                                {
                                    foundname = true;
                                }
                                else
                                {
                                    // No name after period
                                    foundname = false;

                                    // Remove period from identifier
                                    pos--;
                                }
                            }
                            else
                            {
                                // No period after name, so no new name
                                foundname = false;
                            }
                        }

                        // Create token
                        m_tokens.push_back(Token (exstr.substr(start, pos - start), start, pos - 1));

                        // Move pos back so pos++ will set it right
                        pos--;
                    }
                    else if(expreval_isspace (exstr[pos]))
                    {
                        // Do nothing, just ignore white space, but it still
                        // seperates tokens
                    }
                    else
                    {
                        // Unknown token
                        good_parse = false;
                        SHOW_NONFATAL_ERROR ("Unknown token of \"" << exstr[pos] << "\"");
                    }
                }
                break;
            }
        }
    }
}

}
